#pragma semicolon 1
#pragma newdecls required
#include <sourcemod>
#include <sdktools>
#include <adminmenu>
#include <clientprefs>

#define PLUGIN_VERSION "1.0"
#define MAX_SPEED 10.0  // 最大速度限制
#define MIN_SPEED 0.1   // 最小速度限制
#define DEFAULT_SPEED 1.0 // 默认速度值


// 插件基本信息
public Plugin myinfo = 
{
    name = "l4d2_player_speed_adjuster",  // 插件名称
    author = "豆瓣酱な",      // 作者
    description = "管理员菜单设置玩家移动速度.", // 功能描述
    version = PLUGIN_VERSION, // 版本号
    url = "https://gitee.com/sweet-and-sour-bombs/adfvsdsd"  // 项目网址
};

// 全局变量
TopMenu g_topMenu;          // 管理员菜单句柄
Handle g_hSpeedCookie;      // 客户端Cookie句柄

// 预设速度值和显示名称
static const float g_fSpeedPresets[] = {0.1, 0.5, 1.0, 1.1, 2.0, 3.0, 5.0};
static const char g_sSpeedNames[][] = {
    "极慢 (0.1x)",   // 0.1倍速度
    "慢速 (0.5x)",   // 0.5倍速度
    "正常 (1.0x)",   // 1.0倍速度
    "默认 (1.1x)",   // 1.1倍速度(游戏默认)
    "快速 (2.0x)",   // 2.0倍速度
    "极快 (3.0x)",   // 3.0倍速度
    "超快 (5.0x)"    // 5.0倍速度
};

// 插件初始化
public void OnPluginStart()
{
    // 检查clientprefs扩展是否加载
    if (!LibraryExists("clientprefs"))
    {
        SetFailState("此插件需要clientprefs扩展支持");
    }

    // 注册管理员命令
    RegAdminCmd("sm_speed", Command_Speed, ADMFLAG_GENERIC, "打开速度控制菜单");
    RegAdminCmd("sm_setspeed", Command_SetSpeed, ADMFLAG_GENERIC, "设置玩家速度: sm_setspeed <速度> [目标]");
    
    // 创建客户端Cookie用于保存速度设置
    g_hSpeedCookie = RegClientCookie("speed_control", "玩家速度设置", CookieAccess_Protected);
    
    // 挂钩游戏事件
    HookEvent("player_spawn", Event_PlayerSpawn);  // 玩家重生事件
    HookEvent("round_start", Event_RoundStart);    // 回合开始事件
    
    // 加载管理员菜单
    LoadAdminMenu();
    
    // 自动生成配置文件
    AutoExecConfig(true, "plugin.speedcontrol");
}

// 加载管理员菜单
void LoadAdminMenu()
{
    TopMenu topmenu;
    if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != null))
    {
        OnAdminMenuReady(topmenu);
    }
}

// 管理员菜单准备就绪回调
public void OnAdminMenuReady(Handle aTopMenu)
{
    TopMenu topmenu = TopMenu.FromHandle(aTopMenu);

    if (topmenu == g_topMenu)
        return;
    
    g_topMenu = topmenu;
    
    // 将速度控制添加到管理员菜单的"玩家命令"分类下
    TopMenuObject player_commands = FindTopMenuCategory(g_topMenu, ADMINMENU_PLAYERCOMMANDS);
    if (player_commands != INVALID_TOPMENUOBJECT)
    {
        AddToTopMenu(g_topMenu, 
                    "sm_speedcontrol", 
                    TopMenuObject_Item, 
                    AdminMenu_SpeedControl, 
                    player_commands, 
                    "sm_speedcontrol", 
                    ADMFLAG_GENERIC);
    }
}

// 管理员菜单项显示处理
public void AdminMenu_SpeedControl(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
{
    if (action == TopMenuAction_DisplayOption)
    {
        Format(buffer, maxlength, "设置玩家速度");
    }
    else if (action == TopMenuAction_SelectOption)
    {
        DisplaySpeedMenu(param);  // 显示速度控制菜单
    }
}

/* 命令处理函数 */

// 速度菜单命令
public Action Command_Speed(int client, int args)
{
    if (!CheckAdminAccess(client, ADMFLAG_GENERIC))
        return Plugin_Handled;
    
    DisplaySpeedMenu(client);  // 显示主菜单
    return Plugin_Handled;
}

// 直接设置速度命令
public Action Command_SetSpeed(int client, int args)
{
    if (!CheckAdminAccess(client, ADMFLAG_GENERIC))
        return Plugin_Handled;
    
    if (args < 1)
    {
        ReplyToCommand(client, "[SM] 用法: sm_setspeed <速度> [目标]");
        return Plugin_Handled;
    }
    
    char sSpeed[16], sTarget[32];
    GetCmdArg(1, sSpeed, sizeof(sSpeed));  // 获取速度参数
    float speed = StringToFloat(sSpeed);    // 转换为浮点数
    
    // 获取目标参数，默认为自己
    if (args > 1)
    {
        GetCmdArg(2, sTarget, sizeof(sTarget));
    }
    else
    {
        strcopy(sTarget, sizeof(sTarget), "@me");
    }
    
    PerformSpeedSet(client, sTarget, speed);  // 执行速度设置
    return Plugin_Handled;
}

// 执行速度设置
void PerformSpeedSet(int client, const char[] target, float speed)
{
    char target_name[MAX_TARGET_LENGTH];
    int target_list[MAXPLAYERS], target_count;
    bool tn_is_ml;
    
    // 处理目标字符串
    if ((target_count = ProcessTargetString(
            target,
            client,
            target_list,
            MAXPLAYERS,
            COMMAND_FILTER_ALIVE,
            target_name,
            sizeof(target_name),
            tn_is_ml)) <= 0)
    {
        ReplyToTargetError(client, target_count);
        return;
    }
    
    // 对每个目标设置速度
    for (int i = 0; i < target_count; i++)
    {
        if (IsValidClient(target_list[i]))
        {
            SetClientSpeed(target_list[i], speed);
        }
    }
    
    // 显示设置活动
    if (tn_is_ml)
    {
        ShowActivity2(client, "[SM] ", "将 %t 的速度设置为 %.1f", target_name, speed);
    }
    else
    {
        ShowActivity2(client, "[SM] ", "将 %s 的速度设置为 %.1f", target_name, speed);
    }
}

/* 菜单系统 */

// 显示主菜单
void DisplaySpeedMenu(int client)
{
    Menu menu = new Menu(MenuHandler_Speed);
    menu.SetTitle("玩家速度控制");
    
    // 添加预设速度选项
    for (int i = 0; i < sizeof(g_fSpeedPresets); i++)
    {
        char sItem[64], sValue[16];
        Format(sItem, sizeof(sItem), "%s", g_sSpeedNames[i]);
        Format(sValue, sizeof(sValue), "%.1f", g_fSpeedPresets[i]);
        menu.AddItem(sValue, sItem);
    }
    
    // 添加其他选项
    menu.AddItem("custom", "自定义速度...");
    menu.AddItem("reset", "重置为默认速度");
    
    menu.ExitBackButton = true;  // 启用返回按钮
    menu.Display(client, MENU_TIME_FOREVER);  // 显示菜单
}

// 主菜单处理函数
public int MenuHandler_Speed(Menu menu, MenuAction action, int client, int param2)
{
    switch (action)
    {
        case MenuAction_Select:
        {
            char info[32];
            menu.GetItem(param2, info, sizeof(info));
            
            if (StrEqual(info, "custom"))
            {
                DisplayCustomSpeedMenu(client);  // 显示自定义速度面板
            }
            else if (StrEqual(info, "reset"))
            {
                PerformSpeedSet(client, "@me", DEFAULT_SPEED);  // 重置速度
                DisplaySpeedMenu(client);
            }
            else
            {
                float speed = StringToFloat(info);
                DisplayTargetMenu(client, speed);  // 显示目标选择菜单
            }
        }
        case MenuAction_Cancel:
        {
            if (param2 == MenuCancel_ExitBack && g_topMenu != null)
            {
                g_topMenu.Display(client, TopMenuPosition_LastCategory);  // 返回管理员菜单
            }
        }
        case MenuAction_End:
        {
            delete menu;  // 清理菜单内存
        }
    }
    return 0;
}

// 显示自定义速度面板
void DisplayCustomSpeedMenu(int client)
{
    Panel panel = new Panel();
    panel.SetTitle("输入自定义速度值 (0.1-10.0)");
    panel.DrawItem("", ITEMDRAW_SPACER);
    panel.DrawItem("确认", ITEMDRAW_CONTROL);
    panel.DrawItem("取消", ITEMDRAW_CONTROL);
    
    panel.Send(client, PanelHandler_CustomSpeed, MENU_TIME_FOREVER);
    delete panel;
    
    PrintToChat(client, "[SM] 请在聊天框中输入你想要设置的速度值 (0.1-10.0)");
}

// 自定义速度面板处理
public int PanelHandler_CustomSpeed(Handle menu, MenuAction action, int client, int param2)
{
    if (action == MenuAction_Select && param2 == 1)
    {
        FakeClientCommand(client, "sm_setspeed");  // 触发设置速度命令
    }
    return 0;
}

// 显示目标选择菜单
void DisplayTargetMenu(int client, float speed)
{
    Menu menu = new Menu(MenuHandler_Target);
    
    char title[128];
    Format(title, sizeof(title), "选择目标 (速度: %.1fx)", speed);
    menu.SetTitle(title);
    
    menu.AddItem("self", "对自己设置");
    menu.AddItem("all", "所有幸存者");
    menu.AddItem("select", "选择玩家");
    
    char sSpeed[16];
    Format(sSpeed, sizeof(sSpeed), "%.1f", speed);
    menu.AddItem(sSpeed, "", ITEMDRAW_IGNORE);  // 隐藏项，用于传递速度值
    
    menu.ExitBackButton = true;
    menu.Display(client, MENU_TIME_FOREVER);
}

// 目标选择菜单处理
public int MenuHandler_Target(Menu menu, MenuAction action, int client, int param2)
{
    switch (action)
    {
        case MenuAction_Select:
        {
            char info[32], sSpeed[16];
            menu.GetItem(param2, info, sizeof(info));
            menu.GetItem(menu.ItemCount - 1, sSpeed, sizeof(sSpeed));
            float speed = StringToFloat(sSpeed);
            
            if (StrEqual(info, "self"))
            {
                SetClientSpeed(client, speed);
                PrintToChat(client, "[SM] 你的速度设置为 %.1fx", speed);
                DisplaySpeedMenu(client);
            }
            else if (StrEqual(info, "all"))
            {
                // 对所有幸存者设置速度
                for (int i = 1; i <= MaxClients; i++)
                {
                    if (IsValidClient(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i))
                    {
                        SetClientSpeed(i, speed);
                    }
                }
                PrintToChat(client, "[SM] 所有幸存者的速度设置为 %.1fx", speed);
                DisplaySpeedMenu(client);
            }
            else if (StrEqual(info, "select"))
            {
                DisplayPlayerSelectionMenu(client, speed);  // 显示玩家选择菜单
            }
        }
        case MenuAction_Cancel:
        {
            if (param2 == MenuCancel_ExitBack)
            {
                DisplaySpeedMenu(client);  // 返回主菜单
            }
        }
        case MenuAction_End:
        {
            delete menu;
        }
    }
    return 0;
}

// 显示玩家选择菜单
void DisplayPlayerSelectionMenu(int client, float speed)
{
    Menu menu = new Menu(MenuHandler_PlayerSelect);
    
    char title[128];
    Format(title, sizeof(title), "选择玩家 (速度: %.1fx)", speed);
    menu.SetTitle(title);
    
    // 添加所有存活的幸存者玩家
    for (int i = 1; i <= MaxClients; i++)
    {
        if (IsValidClient(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i))
        {
            char userid[12], name[MAX_NAME_LENGTH];
            IntToString(GetClientUserId(i), userid, sizeof(userid));
            GetClientName(i, name, sizeof(name));
            
            menu.AddItem(userid, name);
        }
    }
    
    char sSpeed[16];
    Format(sSpeed, sizeof(sSpeed), "%.1f", speed);
    menu.AddItem(sSpeed, "", ITEMDRAW_IGNORE);
    
    menu.ExitBackButton = true;
    menu.Display(client, MENU_TIME_FOREVER);
}

// 玩家选择菜单处理
public int MenuHandler_PlayerSelect(Menu menu, MenuAction action, int client, int param2)
{
    switch (action)
    {
        case MenuAction_Select:
        {
            char userid[12], sSpeed[16];
            menu.GetItem(param2, userid, sizeof(userid));
            menu.GetItem(menu.ItemCount - 1, sSpeed, sizeof(sSpeed));
            
            int target = GetClientOfUserId(StringToInt(userid));
            float speed = StringToFloat(sSpeed);
            
            if (IsValidClient(target))
            {
                SetClientSpeed(target, speed);
                PrintToChat(client, "[SM] 已设置 %N 的速度为 %.1fx", target, speed);
                PrintToChat(target, "[SM] 管理员将你的速度设置为 %.1fx", speed);
            }
            
            DisplayPlayerSelectionMenu(client, speed);  // 重新显示菜单
        }
        case MenuAction_Cancel:
        {
            if (param2 == MenuCancel_ExitBack)
            {
                char sSpeed[16];
                menu.GetItem(menu.ItemCount - 1, sSpeed, sizeof(sSpeed));
                float speed = StringToFloat(sSpeed);
                
                DisplayTargetMenu(client, speed);  // 返回目标选择菜单
            }
        }
        case MenuAction_End:
        {
            delete menu;
        }
    }
    return 0;
}

/* 核心功能函数 */

// 设置客户端速度
void SetClientSpeed(int client, float speed)
{
    if (!IsValidClient(client))
        return;
    
    // 限制速度范围
    speed = speed < MIN_SPEED ? MIN_SPEED : speed;
    speed = speed > MAX_SPEED ? MAX_SPEED : speed;
    
    // 修改实体属性
    SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", speed);
    
    // 保存到Cookie
    char sSpeed[16];
    Format(sSpeed, sizeof(sSpeed), "%.1f", speed);
    SetClientCookie(client, g_hSpeedCookie, sSpeed);
}

// 重置客户端速度
void ResetClientSpeed(int client)
{
    if (!IsValidClient(client))
        return;
    
    char sSpeed[16];
    GetClientCookie(client, g_hSpeedCookie, sSpeed, sizeof(sSpeed));
    
    // 从Cookie读取速度，如果没有则使用默认值
    float speed = (sSpeed[0] != '\0') ? StringToFloat(sSpeed) : DEFAULT_SPEED;
    SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", speed);
}

// 玩家重生事件
public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast)
{
    int client = GetClientOfUserId(event.GetInt("userid"));
    if (IsValidClient(client))
    {
        ResetClientSpeed(client);  // 重生时重置速度
    }
}

// 回合开始事件
public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
{
    for (int i = 1; i <= MaxClients; i++)
    {
        if (IsValidClient(i) && !IsFakeClient(i))
        {
            ResetClientSpeed(i);  // 新回合重置所有玩家速度
        }
    }
}

// 检查管理员权限
bool CheckAdminAccess(int client, int flags)
{
    if (!client)
    {
        PrintToServer("[SM] 该命令只能在游戏内使用");
        return false;
    }
    
    if (!CheckCommandAccess(client, "sm_speed", flags))
    {
        ReplyToCommand(client, "[SM] 你没有权限使用此命令");
        return false;
    }
    
    return true;
}

// 检查客户端是否有效
bool IsValidClient(int client)
{
    return (client > 0 && client <= MaxClients && IsClientInGame(client));
}